package com.thread.exe;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Memoizer3<A, V> implements Computable<A, V> {
	private final Map<A, FutureTask<V>> cache = new ConcurrentHashMap<A, FutureTask<V>>();
	private final Computable<A, V> c;
	
	public Memoizer3(Computable<A, V> c) {
		super();
		this.c = c;
	}

	@Override
	public V compute(A arg) throws InterruptedException {
		FutureTask<V> v = cache.get(arg);
		if(v == null) {
			Callable<V> task = new Callable<V>() {
				@Override
				public V call() throws Exception {
					V compute = c.compute(arg);
					return compute;
				}
			};
			FutureTask<V> call = new FutureTask<V>(task);
			v = call;
			cache.put(arg, call);
			call.run();
		}
		try {
			return v.get();
		} catch (ExecutionException e) {
			throw launderThrowable(e.getCause());
		}
	}
	
	public static RuntimeException launderThrowable(Throwable t) {
        if(t instanceof RuntimeException)
            return (RuntimeException) t;
        else if(t instanceof Error)
            throw (Error) t;
        else
            throw new IllegalStateException("Not unchected", t);
   }
}
